#include "session.h"
#include "CommandHandler.h"
#include <unistd.h>
#include <algorithm>
#include <sys/socket.h>
#include <cstring>
using namespace std;
using json = nlohmann::json;

extern std::mutex userMapMutex;
extern map<int, int> userMap;

/**
 * @brief Session类构造函数
 */
Session::Session(int socket) : 
    m_db("user.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE)
{
    m_isLogin = -1;
    m_account = -1;
    m_socket = socket;
}

/**
 * @brief Session类析构函数
 */
Session::~Session()
{
    if (m_socket != -1)
    {
        close(m_socket);
        m_socket = -1;
    }
    if (m_isLogin == 1)
    {
        cleanSession();
        m_isLogin = -1;
    }
}

/**
 * @brief 发送消息到当前会话的客户端
 */
void Session::sendMsg(json &j)
{
    std::string msg = j.dump();
    int len = msg.length();
    
    char buffer[4];
    memcpy(buffer, &len, sizeof(len));

    char *message = new char[4 + len];
    memcpy(message, buffer, 4);
    memcpy(message + 4, msg.c_str(), len);

    send(m_socket, message, len + 4, 0);

    std::cout << "send: " << msg << std::endl;
    
    delete[] message;
    message = nullptr;
}

/**
 * @brief 发送消息到指定的socket
 */
void Session::sendMsg(int fd, json &j)
{
    std::string msg = j.dump();
    int len = msg.length();
    
    char buffer[4];
    memcpy(buffer, &len, sizeof(len));

    char *message = new char[4 + len];
    memcpy(message, buffer, 4);
    memcpy(message + 4, msg.c_str(), len);

    send(fd, message, len + 4, 0);

    std::cout << "send to " << fd << ": " << msg << std::endl;
    
    delete[] message;
    message = nullptr;
}

/**
 * @brief 发送消息到多个客户端
 */
void Session::sendMsg(vector<int> fds, json &j)
{
    std::string msg = j.dump();
    int len = msg.length();
    
    char buffer[4];
    memcpy(buffer, &len, sizeof(len));

    char *message = new char[4 + len];
    memcpy(message, buffer, 4);
    memcpy(message + 4, msg.c_str(), len);

    for(auto fd : fds)
    {
        send(fd, message, len + 4, 0);
    }
    
    std::cout << "send group msg to " << fds.size() << " clients: " << msg << std::endl;
    
    delete[] message;
    message = nullptr;
}

/**
 * @brief 接收消息
 */
json Session::recvMsg()
{
    json j;
    int len = 0;
    char buffer[4];
    memset(buffer, 0, 4);

    while (1)
    {
        int res = recv(m_socket, buffer, 4, 0);
        if (res > 0)
            break;
        if (res <= 0)
        {
            if (errno == EWOULDBLOCK || errno == EAGAIN)
                continue;
            j["cmd"] = cmd_logout;
            return j;
        }
    }

    memcpy(&len, buffer, 4);

    char *msg = new char[len + 1];
    memset(msg, 0, len + 1);
    recv(m_socket, msg, len, MSG_WAITALL);

    std::cout << "rsvlen = " << std::to_string(len) << std::endl;
    std::cout << "rsvmsg = " << msg << std::endl;

    try
    {
        j = json::parse(msg);
    }
    catch (nlohmann::json_abi_v3_11_2::detail::parse_error &e)
    {
        std::cerr << "Failed to parse JSON: " << std::endl;
        std::cout << "error json msg : " << msg << endl;
    }

    delete[] msg;
    msg = nullptr;
    
    return j;
}

/**
 * @brief 清理会话状态
 */
void Session::cleanSession()
{
    std::lock_guard<std::mutex> lock(userMapMutex);
    if (userMap.find(m_socket) != userMap.end())
    {
        userMap.erase(m_socket);
    }
}

/**
 * @brief 获取群组成员列表
 */
vector<int> Session::getGroupMember(int groupId)
{
    Statement query(m_db, "SELECT member_id FROM member WHERE group_account=?;");
    query.bind(1, groupId);
    
    vector<int> accounts;
    while(query.executeStep())
    {
        int account = query.getColumn(0).getInt();
        accounts.push_back(account);
    }
    return accounts;
}

/**
 * @brief 获取好友列表
 */
vector<int> Session::getFriendList(int account)
{
    Statement query(m_db, "select account from user where account in (select user2 from friend where user1=? union select user1 from friend where user2=?)");
    query.bind(1, account);
    query.bind(2, account);
    
    vector<int> fds;
    while(query.executeStep())
    {
        int fd = query.getColumn(0).getInt();
        fds.push_back(fd);
    }
    return fds;
}

/**
 * @brief 获取群组列表
 */
vector<int> Session::getGroupList(int account)
{
    Statement query(m_db, "select group_account from member where member_id=?");
    query.bind(1, account);
    
    vector<int> group_accounts;
    while(query.executeStep())
    {
        int group_account = query.getColumn(0).getInt();
        group_accounts.push_back(group_account);
    }
    return group_accounts;
}

/**
 * @brief 获取多个账号对应的socket文件描述符
 */
vector<int> Session::getFriendFd(vector<int> accounts)
{
    std::lock_guard<std::mutex> lock(userMapMutex);
    vector<int> fds;
    
    for(auto account : accounts)
    {
        for(auto it = userMap.begin(); it != userMap.end(); it++)
        {
            if(it->second == account)
            {
                fds.push_back(it->first);
            }
        }
    }
    return fds;
}

/**
 * @brief 获取单个账号对应的socket文件描述符
 */
int Session::getFriendFd(int account)
{
    std::lock_guard<std::mutex> lock(userMapMutex);
    for(auto it = userMap.begin(); it != userMap.end(); it++)
    {
        if(it->second == account)
        {
            return it->first;
        }
    }
    return -1;
}

/**
 * @brief 发送系统消息
 */
void Session::sendSystemMsg(int fd, string msg)
{
    json j;
    j["cmd"] = cmd_friend_chat;
    j["sender"] = 10000;
    j["msg"] = msg;
    sendMsg(fd, j);
}

/**
 * @brief 获取群组创建者的socket文件描述符
 */
int Session::getGroupOwnerFd(int groupId)
{
    Statement query(m_db, "select group_master from group_table where group_account=?");
    query.bind(1, groupId);
    
    int account = -1;
    if(query.executeStep())
    {
        account = query.getColumn(0).getInt();
    }
    return getFriendFd(account);
}

/**
 * @brief 处理接收到的消息
 */
int Session::handleMsg(json msg)
{
    usleep(1000);
    
    int cmd = msg.at("cmd");
    
    SQLite::Database& db = getDatabase();
    
    switch (cmd)
    {
    case cmd_regist:
    {
        string account = msg.at("account");
        string password = msg.at("password");
        string name = msg.at("name");
        
        try {
            int a = stoi(account, 0, 0);
            CommandHandler::Regist(a, password, name, this, db);
        }
        catch(std::invalid_argument &e)
        {
            cout << e.what() << endl;
            return 0;
        }
        break;
    }
    
    case cmd_login:
    {
        string account = msg.at("account");
        string password = msg.at("password");
        
        try {
            int a = stoi(account, 0, 0);
            CommandHandler::Login(a, password, this, db);
        }
        catch(std::invalid_argument &e)
        {
            return 0;
        }
        break;
    }
    
    case cmd_friend_search:
    {
        string info = msg.at("search-info");
        CommandHandler::Search(info, this, db);
        break;
    }
    
    case cmd_add_friend_request:
    {
        int sender = msg.at("sender");
        int account = msg.at("account");
        string msg1 = msg.at("msg");
        
        UserInfo info = {sender, "", msg.at("name"), msg.at("sig"), 1, msg.at("icon")};
        CommandHandler::AddFriendRequest(info, account, msg1, this, db);
        break;
    }
    
    case cmd_add_friend_response:
    {
        int sender = msg.at("sender");
        int account = msg.at("account");
        
        int fd = getFriendFd(sender);
        string res;
        
        if(msg.at("reply") == "yes")
        {
            res = "同意";
            Statement query(db, "insert into friend values(?,?)");
            query.bind(1, sender);
            query.bind(2, account);
            query.exec();
            
            CommandHandler::FriendList(account, this, db);
            CommandHandler::FriendList(sender, this, db);
        }
        else {
            res = "拒绝";
        }
        
        if(fd > 0)
        {
            string msg1 = std::to_string(account) + res + "了你的好友请求";
            sendSystemMsg(fd, msg1);
        }
        break;
    }
    
    case cmd_friend_list:
    {
        usleep(400000);
        int account = msg.at("account");
        CommandHandler::FriendList(account, this, db);
        break;
    }
    
    case cmd_friend_chat:
    {
        int account = msg.at("account");
        int fd = getFriendFd(account);
        
        if(fd > 0)
        {
            sendMsg(fd, msg);
        }
        break;
    }
    
    case cmd_group_search:
    {
        string searchInfo = msg.at("search-info");
        CommandHandler::GroupSearch(searchInfo, this, db);
        break;
    }
    
    case cmd_group_join_request:
    {
        int account = msg.at("account");
        int sender = msg.at("sender");
        string groupname = msg.at("groupName");
        
        UserInfo info = {sender, "", msg.at("name"), msg.at("sig"), 1, msg.at("icon")};
        CommandHandler::GroupJoinReguest(info, account, groupname, msg.at("msg"), this, db);
        break;
    }
    
    case cmd_group_join_response:
    {
        string name = msg.at("name");
        int account = msg.at("account");
        int sender = msg.at("sender");
        
        int fd = getFriendFd(sender);
        sendMsg(fd, msg);
        
        if(msg.at("reply") == "yes")
        {
            sendSystemMsg(fd, "群主同意了你的加群请求");
            
            Statement query(db, "insert into member values(?,?,?)");
            query.bind(1, sender);
            query.bind(2, account);
            query.bind(3, name);
            query.exec();
            
            CommandHandler::GroupList(sender, this, db);
        }
        break;
    }
    
    case cmd_group_list:
    {
        int account = msg.at("account");
        CommandHandler::GroupList(account, this, db);
        break;
    }
    
    case cmd_group_chat:
    {
        int account = msg.at("account");
        vector<int> friendList = getGroupMember(account);
        vector<int> fds = getFriendFd(friendList);
        
        int selfFd = GetSocket();
        auto it = find(fds.begin(), fds.end(), selfFd);
        if (it != fds.end())
        {
            fds.erase(it);
            cout << "skip self" << endl;
        }
        
        sendMsg(fds, msg);
        break;
    }
    
    case cmd_group_member_list:
    {
        int account = msg.at("groupAccount");
        CommandHandler::GroupMemberList(account, this, db);
        break;
    }
    
    case cmd_set_icon:
    {
        int account = msg.at("account");
        string icon = msg.at("icon");
        
        Statement query(db, "update user set icon=? where account=?");
        query.bind(1, icon);
        query.bind(2, account);
        query.exec();
        break;
    }
    }
    
    return 0;
}